/**
 * Copyright(c) 2010 Ceno Techonologies Co., Ltd.
 *
 * History:
 *   15-8-19 上午10:53 Created by lyyang
 */
package com.jade.bss.web.security;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import com.jade.framework.cache.CacheService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;

/**
 * 限制同一用户登录数量filter
 *
 * @author <a href="mailto:lyyang@ceno.cn">lyyang</a>
 * @version 1.0 15-8-19 上午10:53
 */
public class LimitUserSessionFilter extends AccessControlFilter
{

    protected Log logger = LogFactory.getLog(this.getClass());

    private CacheService cacheService;

    private int maxSession = 1;

    private boolean removeFirst = true;

    private SessionManager sessionManager;

    private String logoutUrl;

    private PrincipalUUIDGenerator generator;

    public PrincipalUUIDGenerator getGenerator()
    {
        return generator;
    }

    public void setGenerator(PrincipalUUIDGenerator generator)
    {
        this.generator = generator;
    }

    public String getLogoutUrl()
    {
        return logoutUrl;
    }

    public void setLogoutUrl(String logoutUrl)
    {
        this.logoutUrl = logoutUrl;
    }

    public CacheService getCacheService()
    {
        return cacheService;
    }

    public void setCacheService(CacheService cacheService)
    {
        this.cacheService = cacheService;
    }

    public int getMaxSession()
    {
        return maxSession;
    }

    public void setMaxSession(int maxSession)
    {
        this.maxSession = maxSession;
    }

    public boolean isRemoveFirst()
    {
        return removeFirst;
    }

    public void setRemoveFirst(boolean removeFirst)
    {
        this.removeFirst = removeFirst;
    }

    public SessionManager getSessionManager()
    {
        return sessionManager;
    }

    public void setSessionManager(SessionManager sessionManager)
    {
        this.sessionManager = sessionManager;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o)
            throws Exception
    {
        try {
            Subject subject = getSubject(servletRequest, servletResponse);
            if (!subject.isAuthenticated() && !subject.isRemembered()) {
                //如果没有登录，直接进行之后的流程
                return true;
            }
            Session session = subject.getSession();
            String uuid = generator.getUUId(subject.getPrincipal());
            Serializable sessionId = session.getId();
            check(uuid, sessionId, session);

            return session.getAttribute("logout") == null;
        }
        catch (Throwable ex) {
            ex.printStackTrace();
        }
        return true;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse)
            throws Exception
    {
        Subject subject = getSubject(servletRequest, servletResponse);
        if (subject.isAuthenticated()){
            subject.logout();
        }
        saveRequest(servletRequest);
        WebUtils.issueRedirect(servletRequest, servletResponse, logoutUrl);

        return false;
    }

    protected void check(String uuid, Serializable sessionId, Session session)
    {
        List<Serializable> sessionIds = cacheService.get(uuid);
        if (sessionIds == null) {
            sessionIds = new ArrayList<Serializable>();
            cacheService.set(uuid, sessionIds, 24 * 3600);
        }
        else {
            if (!sessionIds.contains(sessionId) && session.getAttribute("logout") == null) {
                for (Iterator<Serializable> it = sessionIds.iterator(); it.hasNext(); ) {
                    Serializable s = it.next();
                    Session removeSession = null;
                    try {
                        removeSession = sessionManager.getSession(new DefaultSessionKey(s));
                    }
                    catch (SessionException e) {
                        e.printStackTrace();
                    }
                    if (removeSession == null) {
                        logger.debug("session id "+s+" be removed");
                        it.remove();
                    }
                }
                sessionIds.add(sessionId);
                while (sessionIds.size() > maxSession) {
                    Serializable logoutSessionId = null;
                    if (removeFirst) {

                        logoutSessionId = sessionIds.remove(0);

                    }
                    else {
                        logoutSessionId = sessionIds.remove(sessionIds.size() - 1);
                    }
                    Session logoutSession = sessionManager.getSession(new DefaultSessionKey(logoutSessionId));
                    logger.debug("session " + logoutSessionId+" be removed");
                    if (logoutSession != null) {
                        //设置会话的kickout属性表示踢出了
                        logoutSession.setAttribute("logout", "yes");
                    }
                }
                cacheService.set(uuid, sessionIds, 24 * 3600);
            }
        }
    }
}
